#include "net.h"
#include "udp.h"
#include "tcp.h"
#include "icmp.h"
#include "http.h"
#include "driver.h"
#include "time.h"
#include <Windows.h>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat="
#pragma GCC diagnostic ignored "-Wformat-extra-args"


#ifdef UDP
void udp_handler(uint8_t* data, size_t len, uint8_t* src_ip, uint16_t src_port) 
{
    printf("recv udp packet from %s:%u len=%zu\n", iptos(src_ip), src_port, len);
    for (int i = 0; i < len; i++)
        putchar(data[i]);
    putchar('\n');
    udp_send(data, len, 60000, src_ip, src_port); //发送udp包
}
#endif

#ifdef TCP
void tcp_handler(tcp_connect_t* connect, connect_state_t state) {
    uint8_t *buf = (uint8_t *)malloc(BUF_MAX_LEN + 1);
    size_t len = tcp_connect_read(connect, buf, BUF_MAX_LEN);
    buf[len] = 0;
    printf("recv tcp packet from %s:%u len=%zu\n",
        iptos(connect->ip), connect->remote_port, len);
    printf("unack_seq = %d, next_seq = %d\n", connect->unack_seq, connect->next_seq);
    printf("%s\n", buf);
    uint8_t *t = buf;
    // 防止写缓存溢出导致数据被抛弃
    while (len){ 
        size_t sz = tcp_connect_write(connect, t, len);
        t += sz;
        len -= sz;
        net_poll(); // 防止阻塞协议栈导致的死循环
    }
    free(buf);
}
#endif

#ifdef ICMP
map_t icmp_req_map;
clock_t reply_time;
int success;
clock_t min;
clock_t max;
clock_t sum;
uint8_t *ping_ip;
void handler(clock_t start, uint8_t ttl){
    reply_time = clock() - start;
    success++;
    if (reply_time < min)
        min = reply_time;
    if (reply_time > max)
        max = reply_time;
    sum += reply_time;
    printf("Reply from %s :bytes=32 time=%lums TTL=%u\n", iptos(ping_ip), reply_time, ttl);
}
void ping(uint8_t *ip){
    time_t last_time = time(NULL);
    int cnt = 4;
    uint16_t seq = 0;
    success = 0;
    min = 114514;
    max = 0;
    sum = 0;
    ping_ip = ip;
    printf("Ping %s with 32 bytes of data:\n", iptos(ip));
    ping_handler *phandler = malloc(sizeof(void *));
    *phandler = handler;
    while (cnt > 0){
        net_poll();
        if (time(NULL) - last_time >= 1){
            if (cnt < 4){
                if (reply_time == 114514)
                    printf("Request timed out\n");
            }
            reply_time = 114514;
            uint16_t id = GetCurrentProcessId();
            uint32_t key = (id << 16) | seq;
            map_set(&icmp_req_map, &key, phandler);
            icmp_req(id, seq, clock(), ip);
            last_time = time(NULL);
            cnt--;
            seq++;
        }
    }
    while (time(NULL) - last_time < 1)
        net_poll();
    if (reply_time == 114514)
        printf("Request timed out\n");
    printf("Ping statistics for %s:\n", iptos(ip));
    printf("\tPackets:Sent = 4, Received = %d, Lost = %d (%2d%%loss),\n", success, 4-success, (4-success)*(100/4));
    if (success){
        printf("Approximate round trip times in milli-seconds:\n");
        printf("\tMinimum = %lum, Maximum = %lums, Average = %lums\n", min, max, sum/success);
    }
    free(phandler);
}
#endif

int main(int argc, char const *argv[])
{
    if (net_init() != 0)
	{
        printf("net init failed.");
        return -1;
    }
#ifdef UDP
    udp_open(60000, udp_handler); //注册端口的udp监听回调
#endif
#ifdef TCP
    tcp_open(61000, tcp_handler); //注册端口的tcp监听回调
#endif
#ifdef HTTP
    http_server_open(62000);
#endif
#ifdef ICMP
    map_init(&icmp_req_map, sizeof(uint32_t), sizeof(void *), 8, 0, NULL);
    uint8_t ip[NET_IP_LEN] = {192, 168, 56, 1}; 
    ping(ip);
#endif
    while (1) 
	{
        //一次主循环
        net_poll(); //一次主循环
#ifdef HTTP
        http_server_run();
#endif
        // 节约用电
        struct timespec sleepTime = { 0, 1000000 };
        nanosleep(&sleepTime, NULL);
    }
    puts("end");
    return 0;
}
#pragma GCC diagnostic pop
